SnowflakeのAWS PrivateLink設定がセルフサービス対応されたので試してみた
こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。
SnowflakeはSaaSのサービスではありますが、プライベート接続にも対応しており、AWS・Google Cloud、Azureの各クラウドサービスにおけるプライベート接続機能を利用して、Snowflakeとの通信をプライベート化することができます。
この設定については、これまではSnowflakeのサポートに連絡して有効化をしていただく必要がありましたが、なんと6.15のリリースにおいてAWSとAzureでは自分自身で有効化ができるようになりました。
今回は、このAWS PrivateLink設定のセルフサービス設定を実際に試してみたいと思います。
前提と検証範囲について
前提
この機能の前提として、SnowflakeのエディションはBusiness Critical(またはそれ以上)が必要となります。
SnowflakeはAWSのリージョン「ap-northeast-1」(Tokyo)の環境を利用しており、AWSアカウントについては同様に「ap-northeast-1」の環境が既に利用できる環境が整っているものとします。
検証範囲
今回は、以下のドキュメントを参照しながら進めていきます。
一方で、かなり盛り沢山ではあるので以下のポイントに絞って試してみたいと思います。
- AWS PrivateLink の有効化
- AWS VPC 環境の構成
- ステップ1: VPC エンドポイント(VPCE)を作成および構成する
- ステップ2: VPCネットワークを構成する
ですので、今回は「ステップ3: Amazon S3用 AWS VPC インターフェイスエンドポイントを作成する」については扱いません。
セルフサービス設定をしてみる
AWS PrivateLink の有効化
まずは下記のコマンドを実行し、利用するAWSアカウントの一時的なセキュリティクレデンシャルを取得します。
$ aws sts get-federation-token --name sam { "Credentials": { "AccessKeyId": "XXX", "SecretAccessKey": "XXX", "SessionToken": "XXX", "Expiration": "XXX" }, "FederatedUser": { "FederatedUserId": "XXXXXXXXXXXX:sam", "Arn": "arn:aws:sts::XXXXXXXXXXXX:federated-user/sam" }, "PackedPolicySize": 0 }
取得した情報から、FederatedUser.FederatedUserId
に含まれる「12桁のAWSアカウントID」と、Credentials.SessionToken
の値「Federatedトークン」を利用して、以下のクエリをSnowflakeで実行します。
なお、実行時にはACCOUNTADMIN
ロールを利用します。
USE ROLE ACCOUNTADMIN; SELECT SYSTEM$AUTHORIZE_PRIVATELINK( 'XXXXXXXXXXXX', '{ "Credentials": { "AccessKeyId": "XXX", "SecretAccessKey": "XXX", "SessionToken": "XXX", "Expiration": "XXX" }, "FederatedUser": { "FederatedUserId": "XXXXXXXXXXXX:sam", "Arn": "arn:aws:sts::XXXXXXXXXXXX:federated-user/sam" }, "PackedPolicySize": 0 }' );
成功すると、以下のように表示されます。
プライベートリンクへのアクセスが許可されました。
確認として、上記と同様にSYSTEM$GET_PRIVATELINK
関数を利用して確認してみます。
USE ROLE ACCOUNTADMIN; SELECT SYSTEM$GET_PRIVATELINK( 'XXXXXXXXXXXX', '{ "Credentials": { "AccessKeyId": "XXX", "SecretAccessKey": "XXX", "SessionToken": "XXX", "Expiration": "XXX" }, "FederatedUser": { "FederatedUserId": "XXXXXXXXXXXX:sam", "Arn": "arn:aws:sts::XXXXXXXXXXXX:federated-user/sam" }, "PackedPolicySize": 0 }' );
プライベートリンクへのアクセスが許可されました。
大丈夫そうですね。
AWS VPC 環境の構成
次にAWS環境上でVPCエンドポイントを作成しますが、その前にVPCとサブネット、および、サブネット上にEC2インスタンスを立てておきます。
今回は以下のようなCloudFormationテンプレートを利用しました。このテンプレートでは、VPCとパブリックサブネットを作成し、その内部にWindows ServerのEC2インスタンスを作成します。また、その際のEC2インスタンスのキーペアはパラメータストアに保存されるようになっています。
なお、プライベートネットワーク経由でのSnowflake接続確認のためには「プライベートサブネット」がよいのですが、検証の利便性のために「パブリックサブネット」としています。
Windows Serverが用意できたらセキュリティグループにRemote Desktop(RDP)用の通信設定を行い、RDP接続をしてサーバーにアクセスしてブラウザが利用できる状態にしておきます。(後でSnowflakeへのブラウザアクセス確認のために利用します)
simple-windows-server-stack.yml (クリックして展開)
AWSTemplateFormatVersion: '2010-09-09' Description: 'Simple Windows Server Template.' Parameters: # ------------------------------------------------------------# # Common # ------------------------------------------------------------# Prefix: Type: String Default: "prefix" # ------------------------------------------------------------# # Network # ------------------------------------------------------------# VpcCidr: Type: String Default: "10.0.0.0/16" PublicSubnetCidr: Type: String Default: "10.0.0.0/24" # ------------------------------------------------------------# # EC2 # ------------------------------------------------------------# EC2InstanceName: Type: String Default: "ec2" EC2InstanceAMI: Type: AWS::EC2::Image::Id Default: "ami-0ec81697f08841aed" # Windows_Server-2019-Japanese-Full-Base-2022.05.11 EC2InstanceInstanceType: Type: String Default: "t3.micro" EC2InstanceVolumeType: Type: String Default: "gp2" EC2InstanceVolumeSize: Type: String Default: "30" Resources: # ------------------------------------------------------------# # Network # ------------------------------------------------------------# Vpc: Type: AWS::EC2::VPC Properties: CidrBlock: !Ref VpcCidr EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Sub ${Prefix}-vpc InternetGateway: Type: AWS::EC2::InternetGateway DependsOn: Vpc Properties: Tags: - Key: Name Value: !Sub ${Prefix}-ig AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref Vpc InternetGatewayId: !Ref InternetGateway PublicSubnet: Type: AWS::EC2::Subnet Properties: CidrBlock: !Ref PublicSubnetCidr VpcId: !Ref Vpc AvailabilityZone: Fn::Select: - "0" - Fn::GetAZs: "" Tags: - Key: Name Value: !Sub ${Prefix}-public-subnet PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref Vpc Tags: - Key: Name Value: !Sub ${Prefix}-public-rt RouteViaIg: Type: AWS::EC2::Route DependsOn: - AttachGateway Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicSubnetRouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet RouteTableId: !Ref PublicRouteTable SecurityGroup: Type: "AWS::EC2::SecurityGroup" Properties: VpcId: !Ref Vpc GroupName: !Sub "${Prefix}-sg" GroupDescription: "-" Tags: - Key: "Name" Value: !Sub "${Prefix}-sg" # ------------------------------------------------------------# # Ec2InstanceProfile # ------------------------------------------------------------# Ec2Role: Type: AWS::IAM::Role Properties: RoleName: !Sub ${Prefix}-ec2-role AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore Ec2InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: !Sub ${Prefix}-ec2-instance-profile Roles: - !Ref Ec2Role # ------------------------------------------------------------# # EC2Instance # ------------------------------------------------------------# EC2KeyPair: Type: 'AWS::EC2::KeyPair' Properties: KeyName: !Sub ${Prefix}-key-pair EC2Instance: Type: "AWS::EC2::Instance" Properties: Tags: - Key: Name Value: !Sub "${Prefix}-${EC2InstanceName}" KeyName: !Ref EC2KeyPair ImageId: !Ref EC2InstanceAMI InstanceType: !Ref EC2InstanceInstanceType IamInstanceProfile: !Ref Ec2InstanceProfile DisableApiTermination: false EbsOptimized: false BlockDeviceMappings: - DeviceName: /dev/xvda Ebs: DeleteOnTermination: true VolumeType: !Ref EC2InstanceVolumeType VolumeSize: !Ref EC2InstanceVolumeSize SecurityGroupIds: - !Ref SecurityGroup SubnetId: !Ref PublicSubnet UserData: !Base64 | #! /bin/bash yum update -y EC2EIP: Type: AWS::EC2::EIP Properties: InstanceId: Ref: EC2Instance Outputs: VpcId: Value: !Ref Vpc PublicSubnetId: Value: !Ref PublicSubnet EC2ElasticIp: Description: EC2 Elastic IP Value: !Ref EC2EIP
準備ができたら次のステップに進んでいきましょう。
ステップ1: VPC エンドポイント(VPCE)を作成および構成する
Snowflake上でSYSTEM$GET_PRIVATELINK_CONFIG
を呼び出して設定値を取得します。
USE ROLE ACCOUNTADMIN; SELECT SYSTEM$GET_PRIVATELINK_CONFIG();
すると、以下のようなJSONが取得できるのでprivatelink-vpce-id
、privatelink-account-url
、privatelink_ocsp-url
の値を控えておきます。
{ "privatelink-account-name": "XXXXXXX.ap-northeast-1.privatelink", "privatelink-vpce-id": "com.amazonaws.vpce.ap-northeast-1.vpce-svc-XXXXXXXXXXXXXXXXX" "privatelink-account-url": "XXXXXXX.ap-northeast-1.privatelink.snowflakecomputing.com" "regionless-privatelink-account-url": "XXXXXXXXXXXXXXX.privatelink.snowflakecomputing.com" "privatelink_ocsp-url": "ocsp.XXXXXXX.ap-northeast-1.privatelink.snowflakecomputing.com" "privatelink-connection-urls": "[]" }
次に、AWSの管理コンソールからVPCエンドポイントを作成します。
「サービスカテゴリ」は「その他のエンドポイントサービス」とし、「サービス名」に先程控えておいたprivatelink-vpce-id
の値を入力して「サービスの検証」をします。
無事にサービス名が検証されたら、先程作成したVPC、サブネット、セキュリティグループを指定してエンドポイントを作成します。
また、セキュリティグループは本来であれば別途VPCエンドポイント用のセキュリティグループを作成すべきですが、今回は一旦既存のもの(先程作成したセキュリティグループ)を流用することにします。
このセキュリティグループに対しては、80
ポートと443
ポートの接続許可を追加します。接続元は「VPCエンドポイント」に接続するEC2インスタンスなどです。今回はCloudFormationでEC2インスタンスを作成したので、該当EC2インスタンスのプライベートIPアドレスを指定しました。
追加のCNAME記録の作成
最後にDNSまわりの設定です。Snowflakeにアクセスする際にはXXXXXXX.ap-northeast-1.privatelink.snowflakecomputing.com
のような形式でアクセスしますが、この名前解決のためには権威DNSサーバに該当レコードを登録する必要があります。
今回はEC2インスタンスからのアクセスを行って検証したいと思うので、Route53にプライベートホストゾーンを作成していきます。
ドメイン名は以下のようにap-northeast-1.privatelink.snowflakecomputing.com
とし、「プライベートホストゾーン」を選択します。
VPCには先程作成したVPCを指定して「ホストゾーンの作成」を行います。
ホストゾーンが作成できたら、次にレコードを作成していきます。
作成したVPCエンドポイントのDNSの名が、SnowflakeのSYSTEM$GET_PRIVATELINK_CONFIG
で取得したprivatelink-account-url
、privatelink_ocsp-url
で解決できるようにCNAMEのレコードを2つ作成します。
また、同様にapp.ap-northeast-1.privatelink.snowflakecomputing.com
のレコードも作成します。このレコードはオプションのようなのですが、後述の接続確認時にこのアドレスへのアクセスも行っているでしたので、併せて設定しておきます。(初回はSnowsightへのアクセスがあるようです)
作成したら設定が伝播するのを待って接続確認をしてみましょう。
接続してみる
では、実際に接続をしてみます。
作成したEC2インスタンスのWindowsサーバーにRDP接続をし、ブラウザでアクセスしてみます。
アクセス先はSnowflakeのSYSTEM$GET_PRIVATELINK_CONFIG
で取得したhttps://XXXXXXX.ap-northeast-1.privatelink.snowflakecomputing.com
のようなprivatelink-account-url
です。
無事にアクセスできました!
まとめ
以上、SnowflakeのAWS PrivateLink設定がセルフサービス対応されたので試してみました。
サポートケースを上げずに自分自身で設定できるようになっているのでとても助かりますね。また、私自身がSnowflakeのPrivateLink設定自体をしたことが無かったので勉強にもなりました。DNS周りの設定が少し難しかったですが、こちらのサイトを参考にさせていただきうまく設定することができました。(ありがとうございます!)
どなたかのお役に立てば幸いです。それでは!